/*
 * Copyright (c) 2008, 2016, OneAPM and/or its affiliates. All rights reserved.
 */
package me.yufan.chess.ui;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import me.yufan.chess.config.Options;
import org.newdawn.slick.Font;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.UnicodeFont;
import org.newdawn.slick.font.effects.ColorEffect;
import org.newdawn.slick.font.effects.Effect;
import org.newdawn.slick.util.Log;
import org.newdawn.slick.util.ResourceLoader;

import java.awt.*;
import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;

/**
 * Title: Fonts
 * Description: Fonts used for drawing.
 *
 * @author Yufan
 * @version 1.0.0
 * @since 2016-01-26 23:31
 */
public class Fonts {
    public static UnicodeFont DEFAULT, BOLD, X_LARGE, LARGE, MEDIUM, MEDIUM_BOLD, SMALL; // NOSONAR

    /**
     * Set of all Unicode strings already loaded per font.
     */
    private static HashMap<UnicodeFont, HashSet<String>> loadedGlyphs = Maps.newHashMap();

    private Fonts() {
        // This class should not be instantiated.
    }

    /**
     * Initializes all fonts.
     *
     * @throws SlickException      if ASCII glyphs could not be loaded
     * @throws FontFormatException if any font stream data does not contain the required font tables
     * @throws IOException         if a font stream cannot be completely read
     */
    public static void init() throws SlickException, IOException, FontFormatException { // NOSONAR
        float fontBase = 12f * GameImage.getUiScale();
        java.awt.Font javaFont = java.awt.Font.createFont(java.awt.Font.TRUETYPE_FONT,
            ResourceLoader.getResourceAsStream(Options.FONT_NAME));
        java.awt.Font font = javaFont.deriveFont(java.awt.Font.PLAIN, (int) (fontBase * 4 / 3));
        DEFAULT = new UnicodeFont(font);
        BOLD = new UnicodeFont(font.deriveFont(java.awt.Font.BOLD));
        X_LARGE = new UnicodeFont(font.deriveFont(fontBase * 3));
        LARGE = new UnicodeFont(font.deriveFont(fontBase * 2));
        MEDIUM = new UnicodeFont(font.deriveFont(fontBase * 3 / 2));
        MEDIUM_BOLD = new UnicodeFont(font.deriveFont(java.awt.Font.BOLD, fontBase * 3 / 2));
        SMALL = new UnicodeFont(font.deriveFont(fontBase));
        ColorEffect colorEffect = new ColorEffect();
        loadFont(DEFAULT, colorEffect);
        loadFont(BOLD, colorEffect);
        loadFont(X_LARGE, colorEffect);
        loadFont(LARGE, colorEffect);
        loadFont(MEDIUM, colorEffect);
        loadFont(MEDIUM_BOLD, colorEffect);
        loadFont(SMALL, colorEffect);
    }

    /**
     * Loads a Unicode font and its ASCII glyphs.
     *
     * @param font   the font to load
     * @param effect the font effect
     * @throws SlickException if the glyphs could not be loaded
     */
    @SuppressWarnings("unchecked")
    private static void loadFont(UnicodeFont font, Effect effect) throws SlickException { // NOSONAR
        font.addAsciiGlyphs();
        font.getEffects().add(effect);
        font.loadGlyphs();
    }

    /**
     * Adds and loads glyphs for a font.
     *
     * @param font the font to add the glyphs to
     * @param s    the string containing the glyphs to load
     */
    public static void loadGlyphs(UnicodeFont font, String s) {
        if (s == null || s.isEmpty()) {
            return;
        }

        // get set of added strings
        HashSet<String> set = loadedGlyphs.get(font);
        if (set == null) {
            set = Sets.newHashSet();
            loadedGlyphs.put(font, set);
        } else if (set.contains(s)) {
            return;  // string already in set
        }

        // load glyphs
        font.addGlyphs(s);
        set.add(s);
        try {
            font.loadGlyphs();
        } catch (SlickException e) {
            Log.warn(String.format("Failed to load glyphs for string '%s'.", s), e);
        }
    }

    /**
     * Wraps the given string into a list of split lines based on the width.
     *
     * @param font  the font used to draw the string
     * @param text  the text to split
     * @param width the maximum width of a line
     * @return the list of split strings
     * @author davedes (http://slick.ninjacave.com/forum/viewtopic.php?t=3778)
     */
    public static List<String> wrap(Font font, String text, int width) {
        List<String> list = Lists.newArrayList();
        String str = text;
        String line = "";
        int i = 0;
        int lastSpace = -1;
        while (i < str.length()) {
            char c = str.charAt(i);
            if (Character.isWhitespace(c)) {
                lastSpace = i;
            }
            String append = line + c;
            if (font.getWidth(append) > width) {
                int split = (lastSpace != -1) ? lastSpace : i;
                int splitTrimmed = split;
                if (lastSpace != -1 && split < str.length() - 1) {
                    splitTrimmed++;
                }
                list.add(str.substring(0, split));
                str = str.substring(splitTrimmed);
                line = "";
                i = 0;
                lastSpace = -1;
            } else {
                line = append;
                i++;
            }
        }
        if (str.length() != 0) {
            list.add(str);
        }
        return list;
    }
}
